home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / pascal / SWAG9605.DDD / 0034_Improved Graphics Rountines Part 2.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-05-31  |  58.6 KB  |  2,086 lines

  1. {
  2. PART 2 OF NEWGRAPH.PAS
  3.  
  4. You need NEWGRPH1.PAS first. Insert this into the bottom of the
  5. NEWGRPH1.PAS file, compile and use this TPU till yer blue in the
  6. face!!!
  7.  
  8.  
  9.  
  10. ****************
  11. Palette routines
  12. ****************
  13.  
  14. }
  15.  
  16.  
  17.  
  18.  
  19. {
  20. Get the red, green and blue components of a colour.
  21.  
  22. Expects :   ColourNumber is the number of the colour of which you
  23.             want to read the Palette values (0-255).
  24.             RedValue, GreenValue, BlueValue need not be initialised.
  25.  
  26. Returns :   The Red, Green, Blue Values of the colour specified
  27.             by ColourNumber.
  28.  
  29. }
  30.  
  31. Procedure GetPalette(ColourNumber : Byte;
  32.           VAR RedValue, GreenValue, BlueValue : Byte); Assembler;
  33. Asm
  34.    MOV DX,$3C7          { $3C7 is colour ** READ ** select port. }
  35.    MOV AL,ColourNumber   { Select colour to read }
  36.    OUT DX,AL
  37.    ADD DL,2             { DX now = $3C9, which must be read 3 times
  38.                           in order to obtain the Red, Green and
  39.                           Blue values of a colour }
  40.  
  41.    IN AL,DX             { Read red amount. Don't use IN AX,DX as
  42.                           for some strange reason it doesn't work ! }
  43.    LES DI,RedValue
  44.    MOV [ES:DI],AL       { Techie saddos note : STOSB is approx 4 cycles
  45.                           slower and requires double cache multiplex,
  46.                           which basically means "who gives a shit ?". :-)
  47.                         }
  48.  
  49.    IN AL,DX
  50.    LES DI,GreenValue
  51.    MOV [ES:DI],AL
  52.  
  53.    IN AL,DX             { Read blue }
  54.    LES DI,BlueValue
  55.    MOV [ES:DI],AL
  56. End;
  57.  
  58.  
  59.  
  60.  
  61.  
  62. {
  63. This will change the red green and blue components of a colour,
  64. thereby affecting it's shade. How's that for picturesque speech ?
  65.  
  66. Note : You don't need a PaletteType record to use this command,
  67. it affects the screen directly.
  68.  
  69. Expects : ColourNumber is the number of the colour from 0 to 255.
  70.           RedValue is the red component of the colour (0-63).
  71.           GreenValue is the green component of the colour (0-63).
  72.           BlueValue is the blue component of the colour (0-63).
  73.  
  74. Returns : Nothing
  75.  
  76. Corrupts : AL,DX
  77. }
  78.  
  79.  
  80. Procedure SetPalette(ColourNumber, RedValue, GreenValue, BlueValue : Byte); Assembler;
  81. Asm
  82.    MOV AL,ColourNumber
  83.    MOV DX,$3c8          { Write to Port $3C8 with number of Colour to alter }
  84.    OUT DX,AL
  85.    INC DL               { $3C9 again ! }
  86.    MOV AL,RedValue      { Store Red }
  87.    OUT DX,AL
  88.    MOV AL,GreenValue    { Store Green }
  89.    OUT DX,AL
  90.    MOV AL,BlueValue     { Store Blue }
  91.    OUT DX,AL
  92. End;
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99. (*
  100. This Procedure takes a snapshot of all of the colours on screen.
  101.  
  102. Expects : Palette is a variable of type PaletteType which will
  103.           hold the 256 colour palette.
  104.  
  105. Returns : Nothing
  106.  
  107. Notes   : Use this command just before a mode change so that
  108.           you can restore the palette to it's original state
  109.           (via SetAllPalette) at the end of the program.
  110.           (Unless of course you want to corrupt everything)
  111.  
  112. *)
  113.  
  114.  
  115. Procedure GetAllPalette(Var Palette : PaletteType);
  116. Var ColourCount:byte;
  117. Begin
  118.      For ColourCount:=0 to (MaxColours - 1) do
  119.      GetPalette(ColourCount,Palette.RedLevel[ColourCount],
  120.      Palette.GreenLevel[ColourCount],Palette.BlueLevel[ColourCount]);
  121. End;
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129. {
  130. Do I need to explain what this does? It loads in a Palette
  131. from file FileName and stores it in the variable Palette.
  132. Easy enough to use.
  133.  
  134. Expects : FileName is standard MS-DOS filename which refers to the
  135.           palette file.
  136.           Palette is variable of type PaletteType used to hold
  137.           the palette data.
  138.  
  139. Corrupts : Don't know.
  140. }
  141.  
  142. Procedure LoadPalette(FileName: String; Var Palette : PaletteType);
  143. Var PaletteFile: File;
  144. Begin
  145.      Assign(PaletteFile,FileName);
  146.      Reset(PaletteFile,1);
  147.      BlockRead(PaletteFile,Palette,SizeOf(Palette));
  148.      Close(PaletteFile);
  149. End;
  150.  
  151.  
  152.  
  153.  
  154.  
  155. {
  156. Guess what this does then !.
  157.  
  158. Expects : FileName is the MS-DOS file spec of the palette to be saved.
  159.           Palette is the palette to be saved.
  160.  
  161. Returns : Nothing
  162.  
  163. Corrupts : As it's not in assembler it's hard to say, but I guess
  164.            Pascal preserves all registers on entry to a routine ..
  165.            (Don't quote me on that !)
  166. }
  167.  
  168. Procedure SavePalette(FileName: String; Palette : PaletteType);
  169. Var PaletteFile: File;
  170. Begin
  171.      Assign(PaletteFile,FileName);
  172.      Rewrite(PaletteFile,1);
  173.      BlockWrite(PaletteFile,Palette,SizeOf(Palette));
  174.      Close(PaletteFile);
  175. End;
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183. {
  184. This sets the DACs to the Colours specified in your
  185. Palette array. Do NOT alter the Palette data structure
  186. or else this won't work.
  187.  
  188. Expects : Palette is an initialised palette of PaletteType.
  189.  
  190. Returns : Nothing
  191.  
  192. Corrupts : AL, BX, CL, DX, SI, DI, ES
  193. }
  194.  
  195.  
  196. Procedure SetAllPalette(Palette : PaletteType); Assembler;
  197. Asm
  198.    PUSH DS
  199.    LDS BX, Palette      { DS:BX points to Palette record }
  200.    XOR AL,AL
  201.    MOV DX,$3c8          { $3c8 selects the first colour to alter.
  202.                           After 3 writes to $3c9, the VGA automatically
  203.                           moves to the next Colour so there is no
  204.                           need to write to $3c8 again. }
  205.    OUT DX,AL
  206.    INC DL               { Make DX = $3c9, which is used to set the
  207.                           Red / Green and Blue values of a Colour }
  208.  
  209.    MOV CL,(MaxColours-1) { 256 colours }
  210.  
  211.    MOV SI,BX
  212.    ADD SI,MaxColours     { Make SI point to green levels }
  213.    MOV DI,BX
  214.    ADD DI,MaxColours     { Make DI point to blue levels }
  215.    ADD DI,MaxColours
  216.  
  217. {
  218. Note: I read somewhere that some VGA adapters don't like
  219.       being hit with continuous data too quickly..
  220.  
  221.       If not then you should use the BIOS load palette
  222.       function (which will be 20 times slower than this
  223.       hack trick)
  224. }
  225.  
  226. @WritePaletteInfo:
  227.    MOV AL, [BX]         { Read red level from Palette struct }
  228.    OUT DX,AL            { Write to port $3c9 }
  229.    MOV AL, [SI]         { Read green level from Palette struct }
  230.    OUT DX,AL            { Write to port $3c9 }
  231.    MOV AL, [DI]         { Read blue level from Palette struct }
  232.    OUT DX,AL            { Write to port $3c9 }
  233.  
  234.    INC DI               { Next Red part of record }
  235.    INC BX               { Next Green }
  236.    INC SI               { Next Blue }
  237.  
  238.    DEC CL
  239.    CMP CL,$FF               { Dunno if a JNZ works when register is 0
  240.                             or $ff. }
  241.    JNZ @WritePaletteInfo
  242.    POP DS
  243. End;
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250. {
  251. Set the new graphics colour. Also affects text routines as well.
  252.  
  253. Expects : NewColour is the new graphics Colour.
  254.  
  255. Returns : Nothing.
  256.  
  257. Corrupts : AL.
  258. }
  259.  
  260.  
  261. Procedure SetColour(NewColour:byte); Assembler;
  262. Asm
  263.     MOV AL,NewColour
  264.     MOV CurrentColour,AL
  265. End;
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272. {
  273. Get the current graphics colour.
  274. }
  275.  
  276. Function GetColour: byte; Assembler;
  277. Asm
  278.      MOV AL, CurrentColour;
  279. End;
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303.  
  304. {
  305. ****************
  306. Sprite Functions
  307. ****************
  308. }
  309.  
  310.  
  311. {
  312. Get the width of a shape
  313.  
  314. Expects : DataPtr^ points to a shape in memory
  315.  
  316. Returns : Width of shape (1-255)
  317.  
  318. Corrupts : ES, DI
  319. }
  320.  
  321. Function ShapeWidth(Var DataPtr): byte; assembler;
  322. Asm
  323.    LES DI,DataPtr
  324.    MOV AL,[ES:DI]
  325. End;
  326.  
  327.  
  328.  
  329.  
  330. {
  331. Get the height (in pixels) of an Shape.
  332.  
  333. Expects : DataPtr^ points to a shape held in memory
  334.  
  335. Returns : Height of shape (1-255)
  336.  
  337. Corrupts : ES,DI
  338. }
  339.  
  340. Function ShapeHeight(Var DataPtr): byte; assembler;
  341. Asm
  342.    LES DI,DataPtr
  343.    MOV AL,[ES:DI+1]
  344. End;
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352. {
  353. This Function returns the number of bytes required to store
  354. a shape object of a given width and height.
  355.  
  356. Expects : ShapeWidth is the width of the Shape (1-255). You can
  357.           obtain the width of a shape by using the ShapeWidth
  358.           Function above.
  359.  
  360.           Shapeheight is the height of the Shape (1-255). You can
  361.           obtain the height of a shape by using the ShapeHeight
  362.           Function above.
  363.  
  364. Returns : ExtShapeSize = No of bytes shape uses.
  365.  
  366.  
  367. Corrupts : AL,BL.
  368.  
  369. }
  370.  
  371.  
  372. Function ExtShapeSize(ShapeWidth, ShapeHeight : byte): word; Assembler;
  373. Asm
  374.    MOV AL, ShapeWidth
  375.    MOV BL, ShapeHeight
  376.    MUL BL
  377.    INC AX
  378.    INC AX
  379. End;
  380.  
  381.  
  382.  
  383.  
  384. {
  385. Calculate the number of bytes required to hold a shape in memory,
  386. if grabbed from the screen.
  387.  
  388. Expects :     X1, Y1, X2, and Y2 define a rectangular region that
  389.               lies on an imaginary screen (No reading of source/
  390.               dest Bitmap is done!). X1 and X2 must be in the range of
  391.               0-319; Y1 and Y2 must be in the range of 0-199.
  392.  
  393.               You are restricted to images up to 255 x 200 pixels
  394.               in size. (Why 200? Well, you can't grab past the
  395.               vertical limits of the VGA screen can you ?)
  396.  
  397. Returns :     Number of bytes used to hold image. If 0, then this
  398.               means the image is too large to load into a 64K
  399.               portion of RAM.
  400.  
  401. Corrupts :    BX,DX.
  402. }
  403.  
  404. Function ShapeSize(x1,y1,x2,y2:word):word; Assembler;
  405. Asm
  406.      MOV AX,x2          { Width = (X2 - X1) + 1 }
  407.      SUB AX,x1
  408.      INC AX             { Add one extra width byte }
  409.      AND AH,$7F
  410.      OR AH,AH
  411.      JNZ @TooBig
  412.  
  413.      MOV BX,y2          { Height = (Y2 - Y1) + 1 }
  414.      SUB BX,y1
  415.      INC BX
  416.      AND BH,$7F         { And again }
  417.      CMP BX,201
  418.      JB @ShapeFine      { No, shape is OK in width and height }
  419.  
  420. @TooBig:
  421.      XOR AX,AX          { Set AX to return 0, meaning error }
  422.      JMP @Finished
  423.  
  424. @ShapeFine:
  425.      MUL BL             { SpriteDataSize = Width * Height }
  426.      ADD AX,2           { Take into account 2 bytes for Shape "header" }
  427.  
  428. @Finished:
  429. End;
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.  
  440.  
  441. {
  442. Display a shape at a given position on screen, over the current
  443. background (Most games with sprites use this technique). And
  444. if this isn't the fastest sprite routine in the SWAG then I'll
  445. eat my C64.
  446.  
  447. Expects : X and Y specify a horizontal and vertical position for
  448.           the TOP LEFT of an Shape. (Regardless whether or not the
  449.           shape's edge is transparent)
  450.  
  451.           X and Y are presumed ALWAYS valid : i.e. Within bounds of
  452.           screen; Also, it is presumed that the sprite is not placed
  453.           in a position on screen that over runs the screen borders:
  454.           unexpected effects would occur. Sorry! Use ClipBlit if you
  455.           must place sprites in the screen border.
  456.  
  457.           DataPtr, the untyped variable, must point to data for a
  458.           sprite which is up to 254 pixels wide and 200 pixels
  459.           tall.
  460.  
  461. Returns : Nothing
  462.  
  463. Corrupts : AX,BX,CX,DX,SI,DI,ES, & Direction Flag
  464.  
  465. }
  466.  
  467. Procedure Blit(x,y:word; Var DataPtr); Assembler; { A - Ha ! }
  468. Asm
  469.    MOV AX,x
  470.    MOV BX,y
  471.    CALL CalculateOffset         { Calculate where to blit to }
  472.  
  473.    MOV ES,SourceBitmapSegment   { Point ES to source Bitmap }
  474.  
  475.    MOV CX,DS                    { Faster than stack }
  476.    LDS SI,DataPtr               { resides in memory. }
  477.  
  478.    MOV DX,[SI]                  { Get Width into DL and height to DH }
  479.    INC SI                       { Faster than ADD SI,2 - I think }
  480.    INC SI
  481.    CLD                          { Make sure writes are descending }
  482.  
  483.    MOV AH,DL                    { Save width in CL }
  484.  
  485. @Outer:
  486.    MOV DL,AH                    { Reload DL }
  487.    MOV DI,BX                    { DI = Where to write to }
  488.  
  489. { You could use a LODSD, but to be honest it's more trouble than
  490.   it's worth writing the tons of extra code just to save an extra
  491.   clock cycle or two. That is, if it does..
  492. }
  493.  
  494.  
  495. @Main:
  496.    LODSB                { Read byte from DS:SI }
  497.    OR AL,AL             { Is it value 0, meaning transparent ? }
  498.    JZ @NoBlit           { Yes, so ignore byte }
  499.    MOV [ES:DI],AL       { Otherwise write it to the screen. Don't
  500.                           use STOSB ! }
  501.  
  502. @NoBlit:
  503.    INC DI
  504.    DEC DL               { Reduce horizontal counter }
  505.    JNZ @Main            { If not zero then do next byte of the
  506.                           sprite column }
  507.  
  508. @NextScanLine:
  509.    ADD BX,320           { Move down 1 scan line }
  510.    DEC DH               { Reduce vertical count }
  511.    JNZ @Outer           { If not all lines of sprite done back to @Outer }
  512.    MOV DS,CX            { Restore Data Segment }
  513. End;
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522. {
  523. This routine writes a shape to the source Bitmap with no Colour 0
  524. transparency, totally overwriting everything "beneath" it.
  525. Also, there is no clipping of Shape. (Use ClipBlock for this
  526. purpose)
  527.  
  528. Expects  : X and Y specify the horizontal and vertical coordinate
  529.            of the Shape pointed to by DataPtr.
  530.  
  531. Returns  : Nothing
  532.  
  533. Corrupts : AX,BX,CL,DX,SI,DI,ES
  534.  
  535. Notes    : Block is especially useful for "tile" based maps.
  536. }
  537.  
  538. Procedure Block(x,y:word; Var DataPtr); Assembler;
  539. Asm
  540.    MOV AX,x
  541.    MOV BX,y
  542.    CALL CalculateOffset
  543.    CMP BX,-1                    { Off screen ? }
  544.    JZ @StupidUser
  545.  
  546.    PUSH DS                      { Save DS on stack }
  547.    MOV ES,SourceBitmapSegment   { ES: BX -> Where sprite written to }
  548.  
  549.    CLD                          { Make sure writes are descending }
  550.    LDS SI,DataPtr               { This has to be last access of memory
  551.                                   variable as DS is now altered }
  552.    MOV DX,[SI]                  { Get width into DL, height into DH }
  553.    ADD SI,2                     { SI now points to sprite data }
  554.  
  555.  
  556. @Outer:
  557.    MOV DI,BX                    { DI = Offset into VGA screen }
  558.    MOV CL,DL                    { CL = Width of sprite }
  559.  
  560.    CMP CL,4                     { Bytes left < 4 ? }
  561.    JB @CantDoLongWordBlit       { Yeah, so can't do the 4 byte blit }
  562.  
  563.    SHR CL,2                     { Divide Bytes left by 4 }
  564.  
  565. @CopyLong:
  566.    DB $66                       { Otherwise, store longword to [ES:DI] ! }
  567.    MOVSW
  568.    DEC CL                       { CL is long word count }
  569.    JNZ @CopyLong                { If CL <> 0 go back to CopyLong }
  570.  
  571.    MOV CL,DL                    { Restore CL }
  572.    AND CL,3
  573.    OR CL,CL
  574.    JZ @NoBytesLeft
  575.  
  576. @CantDoLongWordBlit:
  577.    CMP CL,2                     { Byte count < 2 ? }
  578.    JB @DoByteBlit               { Yes, can't do a word blit (Shit !)
  579.                                   so that means that there's only
  580.                                   1 byte left. }
  581.  
  582. @CopyWord:
  583.    MOVSW                        { Otherwise, write word }
  584.  
  585. @DoByteBlit:
  586.    TEST CL,1                    { Is there a byte left ? }
  587.    JZ @NoBytesLeft              { No, so no more blits this line }
  588.  
  589.    MOVSB                        { Store the last byte }
  590.  
  591. @NoBytesLeft:
  592.    ADD BX,320                   { Advance BX to next scan line }
  593.    DEC DH                       { Reduce Y count }
  594.    JNZ @Outer                   { if <>0 then go to Outer }
  595.    POP DS                       { Otherwise, restore Data Segment }
  596.  
  597. @StupidUser:
  598. End;
  599.  
  600.  
  601.  
  602.  
  603.  
  604.  
  605.  
  606. {
  607. Perform clipping calculations on an object.
  608.  
  609. Expects : AX to be an X coordinate for a sprite
  610.           BX to be a Y coordinate
  611.           ES:DI to point to the sprite data
  612.  
  613. Returns : If no draw can be done, carry is set TRUE.
  614.           Else carry is FALSE and :
  615.  
  616.           SI will point to first byte to blit
  617.           DI will be the VGA screen offset for first blit
  618.           (ES still is at sprite segment however so must
  619.           be changed afterwards)
  620.           CL is the number of bytes to blit ACROSS
  621.           CH is the number of bytes to blit DOWN
  622.           DX is the MODULO for the image (i.e. how many bytes SI should
  623.           skip (after reload) to get to the start of next row of
  624.           sprite data)
  625.  
  626. Notes :   Unless you are planning to write extra routines which may
  627.           clip images up to 256 x 256 it is wise to leave this Procedure
  628.           as private to the unit as it is quite complex.
  629. }
  630.  
  631.  
  632. Procedure ClipCalculations; Near; Assembler;
  633. Asm
  634.    CMP BX,199              { Y > 199 ? }
  635.    JG @NoDraw              { JG is for SIGNED integers. If Y pos is
  636.                              > 199 then no blit }
  637.  
  638.    CMP AX,319              { X > 319 ? }
  639.    JG @NoDraw              { Yes, Do not do any blits at all }
  640.  
  641.    MOV SI,DI
  642.    INC SI
  643.    INC SI                  { Make SI point to actual sprite data }
  644.  
  645.    XOR CH,CH
  646.    MOV CL,[ES:DI]          { CL holds Clipwidth }
  647.  
  648.    CMP AH,$80              { Quick test if X position is negative }
  649.    JB @XNotNegative        { If not then check if image is off right hand
  650.                              of screen }
  651.    NEG AX                  { Make X position positive }
  652.  
  653.    CMP AX,CX               { If Abs(X) >= Image Width Then Don't Draw }
  654.    JA @NoDraw
  655.  
  656.    SUB CX,AX               { Dec(ClipWidth, Abs(X)) }
  657.    ADD SI,AX               { Inc(DataStart, Abs(X)) }
  658.    XOR AX,AX               { Set X to 0 }
  659.    JMP @NowDoY             { Do Y portion of data now. }
  660.  
  661.  
  662. @XNotNegative:
  663.    MOV DX,CX               { Set DX to clipwidth }
  664.    ADD DX,AX               { If X + ClipWidth < 320 Then }
  665.    CMP DX,320
  666.    JB @NowDoY              { Do Y part (No need to clip width) }
  667.    MOV CX,320
  668.    SUB CX,AX               { ClipWidth = 320 - X }
  669.  
  670.  
  671. {
  672. At this point:
  673.  
  674. AX is the X position of the Shape
  675. BX is the Y position of the Shape
  676. CL is the clipped width of the Shape.
  677.  
  678. Now it is time to do the height part and set the result in
  679. CH.
  680. }
  681.  
  682. @NowDoY:
  683.    XOR DH,DH               { Make DX the height of image }
  684.    MOV DL,[ES:DI+1]
  685.    MOV CH,DL               { Set CH also to height for main blit routine }
  686.  
  687.    CMP BH,$80              { Quick test if Y position is negative }
  688.    JB @YNotNegative
  689.  
  690.    NEG BX                  { Make Y a positive number }
  691.  
  692.    CMP BX,DX               { If Y > ClipHeight }
  693.    JA @NoDraw
  694.    SUB DX,BX               { Dec(ClipHeight, Abs(Y) ) }
  695.    MOV CH,DL               { As an image can only be 255 bytes high
  696.                              this works fine.. }
  697.    PUSH AX                 { Save X Coord on stack }
  698.    XOR AH,AH
  699.    MOV AL,[ES:DI]          { AX = Width }
  700.    MUL BX                  { Calculate Y * Width }
  701.    ADD SI,AX               { Inc(DataStart, Abs(Y) * Width ) }
  702.    POP AX
  703.    XOR BX,BX               { Set Y to 0 }
  704.    JMP @NowDoBlit          { NOW do the blit work. Whew! }
  705.  
  706.  
  707.  
  708. @YNotNegative:
  709.    ADD DX,BX               { If Y + ClipHeight > 199 Then }
  710.    CMP DX,200
  711.    JB @NowDoBlit
  712.    MOV DX,200
  713.    SUB DX,BX               { ClipHeight = 200 - Y }
  714.    MOV CH,DL
  715.  
  716.  
  717. {
  718. At this point AX is the X position
  719.               BX is the Y position
  720.               CL is the ClipWidth and
  721.               CH is the ClipHeight.
  722.  
  723. As the width/height of an Shape can only be an 8 bit
  724. quantity (i.e. < 256) I can discard the H portions of
  725. the registers. Whew!
  726.  
  727. Now follows some weird code.. I'm going to make :
  728.  
  729. DX = Modulo for datastart (which is the width in bytes of Shape.
  730. And yes, I do know that Width could be held in DL but adding extra
  731. code just to satisfy you optimisation junkies is v. boring.)
  732.  
  733. DS:SI already points to data
  734. ES:DI points to active (source) Bitmap
  735.  
  736. }
  737.  
  738. @NowDoBlit:
  739.    PUSH CX                     { Save ClipWidth & ClipHeight on stack }
  740.    CALL CalculateOffset        { Use AX and BX to calculate screen
  741.                                  offset. On exit BX is offset }
  742.    POP CX                      { Restore ClipWidth and ClipHeight }
  743.  
  744.    XOR DH,DH
  745.    MOV DL,[ES:DI]              { DX = Modulo }
  746.    MOV DI,BX                   { Ahhh. Now DI points to the screen offset }
  747.    CLC
  748.    JMP @End
  749.  
  750. @NoDraw:
  751.    STC                         { Indicate no blit possible }
  752.  
  753. @End:
  754. End;
  755.  
  756.  
  757.  
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764. {
  765. This routine does the same as Blit but takes into account
  766. the fact that the sprite may be off the edges of the
  767. screen.
  768.  
  769. Its quite a bit slower than the normal Blit, but that's only
  770. to be expected as there's more computations to be
  771. done.
  772.  
  773. Expects  : X, Y specify the horizontal and vertical position of the Shape,
  774.           DataPtr points to the data to blit.
  775.  
  776. Returns  : Nothing
  777.  
  778. Corrupts : BX,CX,DX,SI,DI,ES.
  779. }
  780.  
  781. Procedure ClipBlit(x,y:integer; Var DataPtr); Assembler;
  782. Asm
  783.    MOV AX,X
  784.    MOV BX,Y
  785.    LES DI,DataPtr
  786.    CALL ClipCalculations
  787.    JC @NoDraw
  788.  
  789.    PUSH DS
  790.    PUSH BP
  791.  
  792.    MOV AX,SourceBitmapSegment
  793.    MOV BX,ES
  794.    MOV DS,BX                   { Now DS: SI points to correct space }
  795.    MOV ES,AX
  796.  
  797.    MOV BX,SI                   { BX to be used to reload SI }
  798.    MOV BP,DI                   { And the screen modulo }
  799.  
  800.    MOV AH,CL                   { AH = Width }
  801.    CLD                         { Make sure LODSB works OK }
  802.  
  803. @Outer:
  804.    MOV CL,AH                   { Re-load CL }
  805.    MOV SI,BX                   { And SI with address of next sprite row }
  806.    MOV DI,BP                   { And DI with address of next scan line }
  807.  
  808.  
  809. @WriteByte:
  810.    LODSB                       { Read byte from DS:SI }
  811.    OR AL,AL                    { Is byte 0 (transparent) ? }
  812.    JZ @NoBlit                  { yes, so don't blit }
  813.    MOV [ES:DI],AL              { Otherwise store byte }
  814.  
  815. @NoBlit:
  816.    INC DI                       { Move DI to next pos. on screen }
  817.    DEC CL                       { Reduce shape width count }
  818.    JNZ @WriteByte               { If not zero, end of shape not reached }
  819.  
  820.    ADD BX,DX                    { BX = BX + Modulo, so BX now points
  821.                                   to first byte of next sprite line
  822.                                   to blit }
  823.    ADD BP,320                   { Make BP point to next line. Note :
  824.                                   If you are going to add some extra
  825.                                   stuff here make sure you're not
  826.                                   accessing local variables! }
  827.  
  828.  
  829.    DEC CH
  830.    JNZ @Outer
  831.  
  832.    POP BP
  833.    POP DS
  834. @NoDraw:
  835. End;
  836.  
  837.  
  838.  
  839.  
  840.  
  841. {
  842. This routine does the same as Block except that it takes into account
  843. that the shape object may be off screen.
  844.  
  845. Expects  : Same as Block.
  846.  
  847. Returns  : Nothing
  848.  
  849. Corrupts : AX,BX,CX,DX,SI,DI,ES are corrupt on exit.
  850. }
  851.  
  852.  
  853.  
  854. Procedure ClipBlock(x,y:integer; Var DataPtr); Assembler;
  855. Asm
  856.    MOV AX,X
  857.    MOV BX,Y
  858.    LES DI,DataPtr          { ES:DI points to data }
  859.    CALL ClipCalculations
  860.    JC @NoDraw
  861.  
  862.  
  863. {
  864. Prepare for blit !
  865. }
  866.  
  867.    PUSH DS
  868.    PUSH BP
  869.  
  870.    MOV AX,SourceBitmapSegment
  871.    MOV BX,ES
  872.    MOV DS,BX                   { Now DS: SI points to correct space }
  873.    MOV ES,AX
  874.  
  875.    MOV BX,SI                   { BX to be used to reload SI (+Image Width) }
  876.    MOV BP,DI                   { And BP to reload DI (+Screen Width) }
  877.  
  878.    CLD                         { Make sure LODSB works OK }
  879.  
  880. @Outer:
  881.    PUSH CX
  882.    MOV CH,CL                   { CH is set to ClipWidth }
  883.  
  884.    MOV SI,BX
  885.    MOV DI,BP
  886.  
  887.    CMP CH,4                     { Bytes left < 4 ? }
  888.    JB @CantDoLongWordBlit       { Yeah, so can't do the 4 byte blit }
  889.  
  890.    SHR CH,2                     { Divide Bytes left by 4 }
  891.  
  892. @CopyLong:
  893.    DB $66                       { Otherwise, store longword to [ES:DI] ! }
  894.    MOVSW
  895.    DEC CH                       { Reduce long word count }
  896.    JNZ @CopyLong
  897.  
  898.    MOV CH,CL                    { Restore CL }
  899.    AND CH,3
  900.    OR CH,CH
  901.    JZ @NoBytesLeft
  902.  
  903.  
  904.  
  905. @CantDoLongWordBlit:
  906.    CMP CH,2                     { Byte count < 2 ? }
  907.    JB @CheckDoByteBlit          { Yes, can't do a word blit (Shit !)
  908.                                   so that means that there's only
  909.                                   1 byte left. }
  910. @CopyWord:
  911.    MOVSW                        { Otherwise, write word }
  912.  
  913.  
  914. @CheckDoByteBlit:
  915.    TEST CH,1                    { Is there a byte left ? }
  916.    JZ @NoBytesLeft              { No, so no more blits this line }
  917.  
  918. @DoByteBlit:
  919.    MOVSB                        { Store the last byte }
  920.  
  921. @NoBytesLeft:
  922.    ADD BX,DX                    { BP to next byte of image to read }
  923.    ADD BP,320                   { Advance BX to next scan line }
  924.  
  925.    POP CX
  926.    DEC CH                       { Reduce Y count }
  927.    JNZ @Outer                   { if <>0 then go to Outer }
  928.  
  929.    POP BP                       { Restore base pointer and }
  930.    POP DS                       { Data Segment }
  931.  
  932. @NoDraw:
  933. End;
  934.  
  935.  
  936.  
  937.  
  938.  
  939.  
  940.  
  941. {
  942. Grab a rectangular area of bytes from the screen for use
  943. as a shape object.
  944.  
  945. Expects     : X1,Y1 define the TOP LEFT of the area to grab.
  946.               X2,Y2 define the BOTTOM RIGHT of the area.
  947.  
  948.               X1 MUST be less than X2;
  949.               Similarly, Y1 MUST be less than Y2.
  950.  
  951.               Also, it is NOT possible to grab an image that
  952.               is more than 255 pixels wide and 200 pixels
  953.               high.
  954.  
  955. Returns     : Nothing
  956.  
  957. Notes       : Use the ShapeSize Function to calculate
  958.               bytes needed to hold shape object in memory .
  959.  
  960. Corrupts    : AX,BX,CX,DX,SI,DI,ES
  961.  
  962. }
  963.  
  964. Procedure GetAShape(x1,y1,x2,y2:word;Var DataPtr); Assembler;
  965. Asm
  966.    MOV AX,x1
  967.    MOV BX,y1
  968.    CALL CalculateOffset
  969.    CMP BX,-1
  970.    JZ @StupidUser
  971.  
  972.    MOV AX,x2                    { Width = (X2 - X1) +1 }
  973.    SUB AX,x1
  974.    INC AX                       { Take into account extra pixel }
  975.    MOV DL,AL
  976.  
  977.    MOV AX,y2                    { Height = (Y2 - Y1) +1 }
  978.    SUB AX,y1
  979.    INC AX
  980.    MOV DH,AL
  981.  
  982.    LES DI,DataPtr
  983.    MOV [ES:DI],DX               { Store Width & Height }
  984.    ADD DI,2
  985.  
  986.    PUSH DS
  987.    MOV DS,SourceBitmapSegment
  988.    CLD                          { Make sure writes are descending }
  989.  
  990.  
  991. @Outer:
  992.    MOV SI,BX                    { SI = Offset into VGA screen }
  993.    MOV CL,DL                    { CL = Width of sprite held in DL }
  994.  
  995.    CMP CL,4                     { Bytes left < 4 ? }
  996.    JB @CantDoLongWordBlit       { Yeah, so can't do the 4 byte blit }
  997.  
  998.    SHR CL,2                     { Divide Count by 4 }
  999.  
  1000. @CopyLong:
  1001.    DB $66                       { Otherwise, store longword to [ES:EDI] ! }
  1002.    MOVSW
  1003.    DEC CL                       { CL is long word count }
  1004.    JNZ @CopyLong                { If CL <> 0 go back to CopyLong }
  1005.  
  1006.    MOV CL,DL                    { Restore CL to width of Shape }
  1007.  
  1008. @CantDoLongWordBlit:
  1009.    AND CL,3
  1010.    OR CL,CL                     { Any bytes left ? }
  1011.    JZ @NoBytesLeft
  1012.  
  1013.    CMP CL,2                     { Byte count < 2 ? }
  1014.    JB @DoByteBlit               { Yes, can't do a word blit (Shit !)
  1015.                                   so that means that there's only
  1016.                                   1 byte left. }
  1017.  
  1018. @CopyWord:
  1019.    MOVSW                        { Otherwise, write word }
  1020.    TEST CL,1
  1021.    JZ @NoBytesLeft              { No, so no more blits this line }
  1022.  
  1023.  
  1024. @DoByteBlit:
  1025.    MOVSB                        { Store the last byte }
  1026.  
  1027. @NoBytesLeft:
  1028.    ADD BX,320                   { Advance BX to next scan line }
  1029.    DEC DH                       { Reduce Y count }
  1030.    JNZ @Outer                   { if <>0 then go to Outer }
  1031.  
  1032.    POP DS                       { Otherwise, restore Data Segment }
  1033.  
  1034. @StupidUser:
  1035. End;
  1036.  
  1037.  
  1038.  
  1039.  
  1040.  
  1041.  
  1042.  
  1043.  
  1044.  
  1045. {
  1046. This routine checks if the data contained within a Shape will
  1047. "Collide" with the background. (Background data is held within
  1048. the Source Bitmap)
  1049.  
  1050. This command is very useful for games that need accurate
  1051. Shape to background collision detection.
  1052.  
  1053. Expects : X and Y specify the horizontal and vertical position
  1054.           of a shape pointed to by DataPtr.
  1055.  
  1056. Returns : If the Shape has collided with ANY background (represented
  1057.           by colours 1-255) on the SOURCE Bitmap then BlitColl is TRUE.
  1058.  
  1059. Corrupts : AX,BX,CX,DX,SI,DI,ES
  1060. }
  1061.  
  1062. Function BlitColl(x,y :integer; Var dataptr) : boolean; Assembler;
  1063. Asm
  1064.    MOV AX,x
  1065.    MOV BX,y
  1066.    CALL CalculateOffset         { On exit, BX will hold screen "Offset" }
  1067.  
  1068.    MOV ES,SourceBitmapSegment
  1069.  
  1070.    PUSH DS
  1071.    PUSH BP
  1072.    LDS SI,DataPtr
  1073.  
  1074.  
  1075.    MOV DX,[SI]             { DL= Width, DH = Height }
  1076.    INC SI
  1077.    INC SI                  { Make SI point to sprite data }
  1078.  
  1079.    CLD                     { Make sure writes are descending }
  1080.  
  1081.    MOV CL,DL
  1082.  
  1083. @Outer:
  1084.    MOV DI,BX               { DI = Offset into Source Bitmap }
  1085.    MOV DL,CL
  1086.  
  1087. { Check if any long words can be checked }
  1088.  
  1089.    CMP DL,4                { Is width at least 4 bytes ? }
  1090.    JB @CantCheckLong       { No }
  1091.    SHR DL,2                { Otherwise, divide width by 4 so that
  1092.                              DL will hold number of LONGs to check }
  1093.  
  1094.  
  1095. @CheckLong:
  1096.    DB $66; LODSW           { LODSD : Load EAX from DS:SI }
  1097.    DB $66; OR AX,AX        { OR EAX,EAX }
  1098.    JZ @NoCheckBackLong     { If EAX is zero then no point in checking
  1099.                              background is there ? }
  1100.  
  1101.    DB $66
  1102.    MOV BP,AX               { Make a copy of EAX }
  1103.    DB $66
  1104.    XOR AX,[ES:DI]          { XOR EAX, [ES:DI]  (Xor EAX with Background) }
  1105.    DB $66
  1106.    CMP BP,AX               { Is EAX unaffected by the XOR - i.e.
  1107.                              No collision }
  1108.    JNZ @CollisionOccurred
  1109.  
  1110.  
  1111. @NoCheckBackLong:
  1112.    ADD DI,4                { Bump DI to next long word }
  1113.    DEC DL                  { Reduce long word count }
  1114.    JNZ @CheckLong          { And now do the collision check for long word }
  1115.  
  1116.  
  1117.  
  1118.    MOV DL,CL               { Restore DL to it's previous contents }
  1119.    AND DL,3                { Mask out all but bits 0 & 1 }
  1120.  
  1121.  
  1122. { Any words left to be checked ? }
  1123.  
  1124. @CantCheckLong:
  1125.    CMP DL,2                { Is there at least 2 bytes left to move ? }
  1126.    JB @CantCheckWord       { No }
  1127.  
  1128. @CheckWord:
  1129.    LODSW                   { Read word from DS:SI into AX }
  1130.    OR AX,AX                { Is Shape data non zero ? }
  1131.    JZ @CantCheckWord       { Yes, so can't be a collision }
  1132.  
  1133.    MOV BP,AX
  1134.    XOR AX,[ES:DI]          { Otherwise, check background too }
  1135.    CMP BP,AX               { Is AX different ? }
  1136.    JNZ @CollisionOccurred  { Yes, so this means a collision }
  1137.    ADD DI,2                { Otherwise add 1 byte }
  1138.  
  1139. @CantCheckWord:
  1140.    TEST CL,1               { Is there a single byte left to check }
  1141.    JZ @AllChecksDone       { Nope }
  1142.    LODSB                   { Otherwise, read it }
  1143.    OR AL,AL                { Zero ? }
  1144.    JZ @AllChecksDone       { Yes, so basically no more checks to do }
  1145.  
  1146.    MOV CH,AL
  1147.    XOR AL,[ES:DI]          { No, so check background byte }
  1148.    CMP CH,AL               { Is AL different ? }
  1149.    JNZ @CollisionOccurred  { Yes, so a collision has occurred }
  1150.  
  1151.  
  1152. @AllChecksDone:
  1153.    ADD BX,320              { 320 is the number of bytes in one scan-line }
  1154.    DEC DH                  { Reduce vertical count (Counts from height of Shape) }
  1155.    JNZ @Outer              { If <>0 then check for next line of Shape }
  1156.    MOV AL,False            { If all lines have been done then this means
  1157.                              that no collision has occurred }
  1158.  
  1159.    JMP @Exit               { And exit. Don't insert a RET here -
  1160.                              you'll crash the program ! }
  1161.  
  1162. @CollisionOccurred:
  1163.    MOV AL,True             { This part is only reached if a collision has
  1164.                              occurred. }
  1165.  
  1166. @Exit:
  1167.    POP BP                 { Restore Base Pointer }
  1168.    POP DS                 { Restore data segment }
  1169. End;
  1170.  
  1171.  
  1172.  
  1173.  
  1174.  
  1175.  
  1176. {
  1177. De-allocate memory for an Shape.
  1178.  
  1179. Expects  : DataPtr is an Shape pointer.
  1180.  
  1181. Returns  : A crash if you're not careful !! :-(
  1182.  
  1183. Corrupts : The assembler part uses AX,DI and ES. Don't know about
  1184.            the Pascal part however.
  1185. }
  1186.  
  1187. Procedure FreeShape(DataPtr:pointer);
  1188. Var ImWidth,
  1189.     ImHeight: byte;
  1190. Begin
  1191.      Asm
  1192.      LES DI,DataPtr
  1193.      MOV AX,[ES:DI]
  1194.      MOV ImWidth,AL
  1195.      MOV ImHeight,AH
  1196.      End;
  1197.      FreeMem(DataPtr,ExtShapeSize(ImWidth,ImHeight));
  1198. End;
  1199.  
  1200.  
  1201.  
  1202.  
  1203.  
  1204.  
  1205. {
  1206. Load in a .IMG file from disk.
  1207.  
  1208. WARNING! This is NOT the IMG file type used by some paint packages!
  1209. It is a non-standard file (albeit very simple) format that NEWGRAPH
  1210. writes, so trying to load a shape created from a paint package
  1211. etc. will not work.
  1212.  
  1213.  
  1214. Expects  :  FileName to be a valid MS-DOS path.
  1215.             DataPtr to be a valid pointer to where data will be stored.
  1216.  
  1217. Returns  :  Nothing, although sprite may have loaded into memory.
  1218.  
  1219. Corrupts :  Don't know.
  1220.  
  1221. }
  1222.  
  1223. Procedure LoadShape(FileName:String; Var DataPtr: Pointer);
  1224. Var F: File;
  1225.     DestSeg,
  1226.     DestOffset,
  1227.     ImgSize: word;
  1228.     ShapeWidth,
  1229.     ShapeHeight: byte;
  1230.  
  1231. Begin
  1232.      Assign(F,FileName);
  1233.      Reset(F,1);
  1234.      BlockRead(F,ShapeWidth,1);       { Read in width & height }
  1235.      BlockRead(F,ShapeHeight,1);
  1236.  
  1237.  
  1238.      {
  1239.      Calculate number of bytes that need to be reserved for the
  1240.      Shape.
  1241.      }
  1242.  
  1243.      ImgSize:= ExtShapeSize(ShapeWidth,ShapeHeight);
  1244.      If ImgSize < MaxAvail Then
  1245.         Begin
  1246.  
  1247.         GetMem(DataPtr,ImgSize);
  1248.         GetPtrData(DataPtr,DestSeg,DestOffset);
  1249.  
  1250.         Reset(F,1);
  1251.         BlockRead(F,Mem[DestSeg:DestOffset], ImgSize);
  1252.         Close(F);
  1253.         End
  1254.      Else
  1255.          Asm
  1256.          DB $66
  1257.          MOV WORD [OFFSET DataPtr],0         { Signal no memory claimed }
  1258.          DW 0
  1259.      End;
  1260.  
  1261. End;
  1262.  
  1263.  
  1264.  
  1265.  
  1266.  
  1267.  
  1268.  
  1269.  
  1270. {
  1271. Write an Shape to disk, where you could convert it if you like
  1272. to a PCX. With the reg. version, there is a command to do this.
  1273.  
  1274. Expects :  FileName is a standard DOS filename.
  1275.            P is a pointer to where the sprite data exists in memory.
  1276.  
  1277. Returns :  Nothing.
  1278.  
  1279. Corrupts : Don't know.
  1280. }
  1281.  
  1282. Procedure SaveShape(FileName:string; DataPtr:Pointer);
  1283. Var F: File;
  1284.     SourceSeg, SourceOffset: word;
  1285. Begin
  1286.      Assign(F,FileName);
  1287.      Rewrite(F,1);
  1288.      GetPtrData(DataPtr,SourceSeg,SourceOffset);
  1289.  
  1290.      BlockWrite(F, Mem[SourceSeg:SourceOffset],
  1291.                    ExtShapeSize(mem[SourceSeg:SourceOffset],
  1292.                    mem[SourceSeg:SourceOffset+1]));
  1293.      Close(F);
  1294. End;
  1295.  
  1296.  
  1297.  
  1298.  
  1299.  
  1300.  
  1301.  
  1302.  
  1303.  
  1304. {
  1305. ***************************************
  1306. PCX LOAD AND SAVE ROUTINES - WHICH WORK
  1307. ***************************************
  1308. }
  1309.  
  1310.  
  1311.  
  1312. {
  1313. This will put a mode 13h 256 colour PCX at position X,Y and
  1314. show a defined area. Useful for low res multimedia applications. :-)
  1315. This PCX loader can handle PCX's of variable dimensions up to
  1316. width 320 and height 200 so you could design sprites
  1317. with a graphics package and save them as a PCX then grab them
  1318. off the screen as Shapes. Also, this PCX loader is far faster than
  1319. Norman Yen's effort and intelligently uses memory. (Note: How can
  1320. a program dumbly use memory? Hmm?)
  1321.  
  1322. Expects: Filename is an MS-DOS filespec relating to the PCX's name,
  1323.          i.e. 'C:\WORK\SHEEP.PCX' (Oh well explained Scott :^( )
  1324.          ThePalette is a PaletteType record used to hold the PCX's
  1325.          palette data.
  1326.          X,Y specifies the top left coordinates on screen of where
  1327.          the PCX is to be drawn. X should be in the range of 0 to
  1328.          319, Y should be in the range of 0 to 199. The picture
  1329.          will be clipped as necessary.
  1330.  
  1331. Returns: Your program will halt with an error message if the PCX file
  1332.          does not exist, or if the PCX is not of the correct "type".
  1333.          (I.E. It's not mode 13h or it's not 256 colour etc.).
  1334. }
  1335.  
  1336. Procedure LocatePCX(filename:string; Var ThePalette: PaletteType;
  1337.           x,y,widthtoshow,heighttoshow:word);
  1338.  
  1339. var PCXFile: file;
  1340.  
  1341.     ReadingFromMem  : Boolean;      { If True it means All/Some PCX
  1342.                                       Data is in RAM }
  1343.     MemRequired     : longint;      { Size of PCX bitmap data }
  1344.     BytesRead       : longint;      { Number of PCX bytes read }
  1345.     PCXFileSize     : longint;      { How many bytes PCX uses }
  1346.     Count           : integer;      { I is a general counter used to set
  1347.                                       the PCX's palette and then count
  1348.                                       scan lines }
  1349.     RedVal          : byte;         { Used for ColourMap, Palette values }
  1350.     GreenVal        : byte;         { which define a colour }
  1351.     BlueVal         : byte;
  1352.  
  1353.     MemoryAccessVar : pointer;      { Pointer to read bitmap data }
  1354.     BufferSeg,                      { Where PCX will be loaded to }
  1355.     BufferOffset    : word;
  1356.  
  1357.     VidOffset       : word;         { Screen offset }
  1358.  
  1359.     Width,Height,                   { Width is number of horizontal bytes to grab
  1360.                                       Height is number of vertical bytes to grab }
  1361.     N,Bytes             : word;     { N counts up to Bytes }
  1362.     RunLength,c     : byte;         { RunLength is the Run Length Encoding
  1363.                                       byte, C is the character read from
  1364.                                       PCX data }
  1365.     PastHorizontalLimit : boolean;  { Set true this means no more
  1366.                                      horizontal pixel writes to do, advance 
  1367.                                      to next line as soon as poss.}
  1368.  
  1369. begin
  1370.     assign(PCXFile,FileName);
  1371.  
  1372. {$i-}
  1373.     reset (PCXFile,1);
  1374. {$i+}
  1375.     If IOResult = 0 Then
  1376.        Begin
  1377.  
  1378.        blockread (PCXFile, header, sizeof (header));       { Read in PCX header }
  1379.  
  1380.        if (header.manufacturer=10) and (header.version=5) and
  1381.           (header.bits_per_pixel=8) and (header.colour_planes=1) then
  1382.           begin
  1383.                seek (PCXFile, filesize (PCXFile)-769);     { Move to palette data }
  1384.                blockread (PCXFile, c, 1);                  { Read Colourmap type }
  1385.                if (c=12) then                              { 12 is correct type }
  1386.                begin
  1387.                     {
  1388.                     Read palette data and write to palette
  1389.                     structure.
  1390.                     }
  1391.  
  1392.                     for Count:=0 to 255 do
  1393.                         Begin
  1394.                           BlockRead(PCXFile,RedVal,1);
  1395.                           BlockRead(PCXFile,GreenVal,1);
  1396.                           BlockRead(PCXFile,BlueVal,1);
  1397.  
  1398.                           ThePalette.RedLevel[Count]:=RedVal SHR 2;
  1399.                           ThePalette.GreenLevel[Count]:=GreenVal SHR 2;
  1400.                           ThePalette.BlueLevel[Count]:=BlueVal SHR 2;
  1401.                       End;
  1402.  
  1403.  
  1404.                   seek (PCXFile, 128);
  1405.  
  1406.                   {
  1407.                   If entire size of PCX is less than 64K in length then
  1408.                   it can be stored in a memory buffer and uncompacted
  1409.                   from there. However, if PCX exceeds 64K then it must
  1410.                   be split into several chunks. If your machine does
  1411.                   not have 64K left for the buffer used (You're in trouble !!)
  1412.                   then the system will read the PCX from disk continually,
  1413.                   which works OK but is very slow. So there.
  1414.                   }
  1415.  
  1416.                   MemRequired:=Filesize(PCXFile)-897;
  1417.                   PCXFileSize:=MemRequired;
  1418.                   BytesRead:=0;
  1419.  
  1420.                   If (MemRequired < 65528) And (MaxAvail > MemRequired) Then
  1421.                      Begin
  1422.                      getmem(MemoryAccessVar,MemRequired);
  1423.                      GetPtrData(MemoryAccessVar, BufferSeg, BufferOffset);
  1424.                      BlockRead(PCXFile,Mem[BufferSeg:BufferOffset],MemRequired);
  1425.                      ReadingFromMem:=True;
  1426.                      End
  1427.                   Else
  1428.  
  1429.                   {
  1430.                   If the PCX occupies more than approx. 64K bytes then it
  1431.                   is necessary to read the data into memory in 64K chunks
  1432.                   which is still considerably faster than the
  1433.                   final method (continual reading from disk)
  1434.                   }
  1435.  
  1436.                       If (MaxAvail > 65527) Then
  1437.                          Begin
  1438.                          GetMem(MemoryAccessVar,65528);
  1439.                          GetPtrData(MemoryAccessVar, BufferSeg, BufferOffset);
  1440.                          BlockRead(PCXFile,Mem[BufferSeg:BufferOffset],65528);
  1441.                          BytesRead:=65528;
  1442.                          MemRequired:=65528;
  1443.                          ReadingFromMem:=True;
  1444.                          End
  1445.                       Else
  1446.                           { CLUCK!! Oh well, system is just going to have
  1447.                           to read from disk as there is not even 64K
  1448.                           memory left. (A very bad situation) }
  1449.  
  1450.                           ReadingFromMem:=False;
  1451.  
  1452.                   {
  1453.                   Find out width & height of PCX.
  1454.                   }
  1455.  
  1456.                   width:=(header.xmax - header.xmin)+1;
  1457.                   height:=(header.ymax - header.ymin)+1;
  1458.                   bytes:=header.bytes_per_line;
  1459.  
  1460.                   {
  1461.                   Adjust width & height of PCX if necessary so that PCX
  1462.                   "fits" on screen.
  1463.  
  1464.                   }
  1465.  
  1466.                   if widthtoshow > width Then
  1467.                      widthtoshow:=width;
  1468.  
  1469.                   if (widthtoshow + x) > 320 Then
  1470.                      widthtoshow:=width-x;
  1471.  
  1472.                   if heighttoshow > height Then
  1473.                      heighttoshow:=height;
  1474.  
  1475.                   if (heighttoshow + y)> 200 Then
  1476.                      heighttoshow:=height-y;
  1477.  
  1478.  
  1479.                   {
  1480.                   Do all scan lines.
  1481.                   }
  1482.  
  1483.                   for Count:=0 to (heighttoshow-1) do
  1484.                   begin
  1485.                       n:=0;
  1486.                       PastHorizontalLimit:=False;
  1487.                       vidoffset:= SourceBitmapOffset+((Y+Count)* 320)+X;
  1488.  
  1489.                       while (n<bytes) do
  1490.                       begin
  1491.  
  1492.                            { Display any more pixels width wise from PCX ? }
  1493.  
  1494.                            If N >= WidthToShow Then
  1495.                               PastHorizontalLimit:=True;
  1496.  
  1497.                            If ReadingFromMem Then
  1498.                                Begin
  1499.                                c:=Mem[BufferSeg:BufferOffset];
  1500.                                Inc(BufferOffset);
  1501.                                If BufferOffset = 65528 Then
  1502.                                   Begin
  1503.                                   { End of buffer has been reached, so
  1504.                                     it's time to load another part of the
  1505.                                     PCX }
  1506.  
  1507.                                   If (PCXFileSize - BytesRead)> 65527 Then
  1508.                                      Begin
  1509.                                      BlockRead(PCXFile,Mem[BufferSeg:0],65528);
  1510.                                      Inc(BytesRead,65528);
  1511.                                      End
  1512.                                   Else
  1513.                                       { Load last chunk of PCX }
  1514.  
  1515.                                       Begin
  1516.                                       BlockRead(PCXFile,Mem[BufferSeg:0],
  1517.                                       (PCXFileSize - BytesRead));
  1518.                                       End;
  1519.  
  1520.                                   {
  1521.                                   Now reset buffer pointer to start
  1522.                                   }
  1523.  
  1524.                                   BufferOffset:=0;
  1525.                                   End;
  1526.                                End
  1527.                             Else
  1528.                                 BlockRead(PCXFile,c,1);
  1529.  
  1530. {
  1531. At this point one element of data has been read, and stored in
  1532. variable C. If bits 6 & 7 of C are set then this means to the system
  1533. a "run of bytes" has been found. (i.e. a number sequence - for example,
  1534. four 1's, twenty 15's, any sequence of identical numbers).
  1535.  
  1536. In this case, the 6 least significant bits of C indicate how long the run
  1537. of bytes is. For example, if a sequence of five bytes has been found
  1538. the run = 5. Of course, using 6 bits limits you to a maximum run length
  1539. of 63 bytes but that should be more than enough for most pictures.
  1540.  
  1541. Quite a simple method of compaction eh? Definitely the easiest format to
  1542. understand!
  1543.  
  1544. }
  1545.  
  1546.                             if ((c and 192)=192) then
  1547.                             begin
  1548.  
  1549.                                { Get the 6 least significant bits }
  1550.                                RunLength:=c and 63;
  1551.  
  1552.                                { get the run byte }
  1553.  
  1554.                                If ReadingFromMem Then
  1555.                                   Begin
  1556.                                   c:=Mem[BufferSeg:BufferOffset];
  1557.                                   Inc(BufferOffset);
  1558.  
  1559.                                { Time to read in more data from disk ? }
  1560.  
  1561.                                   If BufferOffset = 65528 Then
  1562.                                      Begin
  1563.                                      If (PCXFileSize - BytesRead)> 65527 Then
  1564.                                         Begin
  1565.                                         BlockRead(PCXFile,Mem[BufferSeg:0],65528);
  1566.                                         Inc(BytesRead,65528);
  1567.                                         End
  1568.                                      Else
  1569.                                          Begin
  1570.                                          BlockRead(PCXFile,Mem[BufferSeg:0],
  1571.                                          (PCXFileSize - BytesRead));
  1572.                                      End;
  1573.  
  1574.                                      BufferOffset:=0;
  1575.                                      End;
  1576.                                   End
  1577.                                Else
  1578.                                    BlockRead(PCXFile,c,1);
  1579.  
  1580.                                {
  1581.                                Can't do blit if past the horizontal limit
  1582.                                of the window.
  1583.                                }
  1584.  
  1585.                                If Not PastHorizontalLimit Then
  1586.                                   Begin
  1587.                                   If n+RunLength > widthtoshow Then
  1588.                                      fillchar(Mem[SourceBitmapSegment:VidOffset],WidthToShow-n,c)
  1589.                                   else
  1590.                                       fillchar(Mem[SourceBitmapSegment:VidOffset],RunLength,c);
  1591.  
  1592.                                   inc(vidoffset,RunLength);
  1593.                                End;
  1594.  
  1595.                                inc(n,RunLength);
  1596.                                end
  1597.                             else
  1598.                                 begin
  1599.                                 If Not PastHorizontalLimit Then
  1600.                                    Begin
  1601.                                    mem [SourceBitmapSegment:vidoffset]:=c;
  1602.                                    inc (vidoffset);
  1603.                                 End;
  1604.                                 inc (n);
  1605.                             end;
  1606.  
  1607.                       end;
  1608.  
  1609.                   end;
  1610.  
  1611.                   If ReadingFromMem Then
  1612.                      freemem(MemoryAccessVar,MemRequired);
  1613.                end
  1614.           else
  1615.               Begin
  1616.               DirectVideo:=False;
  1617.               Writeln('The PCX''s ColourMap is not of the correct type !');
  1618.               Close(PCXFile);
  1619.               Halt(0);
  1620.               End;
  1621.           end
  1622.        Else
  1623.            Begin
  1624.            DirectVideo:=False;
  1625.            Writeln('PCX unsuitable for loading.');
  1626.            Close(PCXFile);
  1627.            Halt(0);
  1628.        End;
  1629.  
  1630.        close (PCXFile);  { Do this anyway ! }
  1631.  
  1632.        end
  1633.     Else
  1634.         Begin
  1635.         DirectVideo:=False;
  1636.         Writeln('File not found ?');
  1637.         Close(PCXFile);
  1638.         Halt(0);
  1639.         End;
  1640.  
  1641. end;
  1642.  
  1643.  
  1644.  
  1645.  
  1646.  
  1647.  
  1648.  
  1649.  
  1650.  
  1651.  
  1652.  
  1653.  
  1654.  
  1655. {
  1656. What this does is load a PCX at the TOP LEFT of the source Bitmap,
  1657. very quickly. If you need to put the PCX somewhere else use LocatePCX.
  1658.  
  1659.  
  1660. Expects:  FileName to be a standard MS-DOS filename, relating to a
  1661.           320 x 200 PCX.
  1662.           ThePalette to be of type Palette. This holds the colour
  1663.           information of the PCX file you are loading.
  1664.  
  1665. You can then use SetAllPalette to set the VGA palette so that
  1666. the pic can display properly.
  1667. }
  1668.  
  1669. Procedure LoadPCX(FileName:string; Var ThePalette: PaletteType);
  1670. Begin
  1671.      LocatePCX(Filename,ThePalette,0,0,320,200);
  1672. End;
  1673.  
  1674.  
  1675.  
  1676.  
  1677.  
  1678.  
  1679. {
  1680. Home grown PCX packer.
  1681.  
  1682. This PCX routine is able to cope with the full 256 colours,
  1683. unlike some other SWAG PCX packers I could mention.. !
  1684.  
  1685. Expects:    FileName is the name of the PCX to save.
  1686.             ThePalette is a PaletteType variable, which has been
  1687.             initialised by, for example, the GetAllPalette routine.
  1688.             X,Y specify the horizontal and vertical positions of where to
  1689.             begin grabbing the PCX data from.
  1690.             PCXWidth and PCXHeight specify the width & height of the
  1691.             window to grab. Easy eh?
  1692.  
  1693.             For example, to grab one half of the VGA screen you could use:
  1694.             SaveAreaAsPCX('1STHALF.PCX',MyPalette,0,0,160,200);
  1695.  
  1696.             And the other half with :
  1697.  
  1698.             SaveAreaAsPCX('2NDHALF.PCX',MyPalette,160,0,160,200);
  1699.  
  1700.             These files can then be loaded into a paint package such
  1701.             as PC Paintbrush or Neopaint (great program!) and manipulated.
  1702.  
  1703.             Use the SAVEPCX routine below to save an entire PCX screen.
  1704.  
  1705.  
  1706. Returns:    Program will halt if the PCX is not found.
  1707.  
  1708.  
  1709. P.S. This routine manages to save a 256 colour screen properly,
  1710.      unlike some other PCX writing routines I could mention. Do you
  1711.      programmers actually TEST your code before sending it into the
  1712.      SWAG ? (Like, are there any GIF loaders that work ?!!)
  1713. }
  1714.  
  1715.  
  1716. Procedure SaveAreaAsPCX(filename:string;ThePalette: PaletteType;
  1717.           x,y, PCXWidth,PCXHeight: word);
  1718.  
  1719. Var f: File;                    { File for writing PCX to }
  1720.     ColourMapID: byte;           { Always holds 12, for the PCX }
  1721.     ColourCount: byte;           { Counts up to number of colours on
  1722.                                   screen (255) }
  1723.     RedValue: byte;             { Palette Values of a colour }
  1724.     GreenValue: byte;
  1725.     BlueValue: byte;
  1726.  
  1727.     LastOffset: word;           { Used as a latch for VidOffset }
  1728.     VidOffset: word;            { Offset into Source Bitmap }
  1729.     VerticalCount: byte;        { Number of scan lines to use }
  1730.     LastByte : byte;            { The last byte read from Source Bitmap }
  1731.     NewByte: byte;              { The current byte }
  1732.     RunLength : byte;           { Counter for run length compression }
  1733.     ByteCount: word;            { Counts up to bytes per scan line (320) }
  1734.  
  1735.  
  1736.  
  1737. Begin
  1738.      Assign(f,filename);
  1739.      Rewrite(f,1);
  1740.  
  1741.      With header do
  1742.      Begin
  1743.           Manufacturer := 10;
  1744.           Version := 5;
  1745.           Encoding :=0;
  1746.           Bits_per_pixel:=8;    { 8 bits = 256 colours }
  1747.           XMin:=0;
  1748.           YMin:=0;
  1749.  
  1750.           {
  1751.           Can't save a PCX more than 320 x 200 in size.
  1752.           }
  1753.  
  1754.           if (PCXwidth + x) > 320 Then
  1755.              PCXwidth:=320-x;
  1756.           if (PCXheight+ y) > 200 Then
  1757.              PCXheight:=200-y;
  1758.  
  1759.           XMax:=(PCXWidth-1);
  1760.           YMax:=(PCXHeight-1);
  1761.           Hres:=320;                        { Hres/Vres could be used to
  1762.                                               determine screen mode -
  1763.                                               probably :-( }
  1764.           VRes:=200;
  1765.  
  1766.           Colour_planes:=1;                 { Mode 13h is not planar }
  1767.           Bytes_per_line:=PCXWidth;         { One byte per pixel }
  1768.           Palette_type:=12;                 { Dunno what 12 is for }
  1769.      End;
  1770.  
  1771.      BlockWrite(F,Header,SizeOf(Header));
  1772.  
  1773.      Asm
  1774.      MOV AX,X
  1775.      MOV BX,Y
  1776.      CALL CalculateOffset
  1777.      MOV VidOffset,BX
  1778.      End;
  1779.  
  1780.      For VerticalCount:=0 to PCXHeight-1 do
  1781.      Begin
  1782.           LastOffset:=VidOffset;
  1783.           ByteCount:=0;
  1784.           LastByte:=0;
  1785.  
  1786.           Repeat
  1787.                 NewByte:=Mem[SourceBitmapSegment:Vidoffset];
  1788.  
  1789.                 {
  1790.                 If the last byte read is equal to the new byte read
  1791.                 then a run of bytes has been identified and so the
  1792.                 system needs to count how many identical bytes (up
  1793.                 to a total of 63) follow. When finished, the
  1794.                 system writes this count to disk PLUS a value of
  1795.                 192 (which is the signal to the PCX reader that
  1796.                 a run of bytes follows) then writes the byte that
  1797.                 was prevalent in the run.
  1798.  
  1799.                 For example, say in the data stream there were 10
  1800.                 values :
  1801.  
  1802.                 0 1 2 6 9 8 7 7 7 4
  1803.  
  1804.                 When the system gets to 8 it would then compare
  1805.                 that number with the next value (7) and see that 8 is
  1806.                 not equal to 7, then the computer would move to said 7
  1807.                 (after the 8) and compare it to the next digit, which
  1808.                 is also a 7.
  1809.  
  1810.                 As a match has been found, the system counts the
  1811.                 number of 7s there, which is (all together now !)
  1812.                 3!! and then adds 192 to the result.. to give 195.
  1813.  
  1814.                 As stated before, bits 6 + 7 of the byte have
  1815.                 been set in order to "flag" to the PCX reader that
  1816.                 a run of bytes have been found.
  1817.  
  1818.                 The value 195 is written to disk, then value 7 so the
  1819.                 PCX reader that loads this file knows what value (and
  1820.                 how many times) to write to the screen during unpacking.
  1821.  
  1822.                 I hope this has explained one of the PCX mysteries. If
  1823.                 it hasn't I typed all that for nothing!! :-)
  1824.                 }
  1825.  
  1826.                 If NewByte = LastByte Then
  1827.                    Begin
  1828.  
  1829.                    RunLength:=0;
  1830.                    While (NewByte = LastByte) and (RunLength < 63)
  1831.                       and (ByteCount <> PCXWidth) do
  1832.                       Begin
  1833.                       Inc(RunLength);
  1834.                       Inc(ByteCount);
  1835.  
  1836.                       {
  1837.                       Move to next byte on Source Bitmap
  1838.                       }
  1839.  
  1840.                       Inc(vidoffset);
  1841.  
  1842.                       NewByte:=Mem[SourceBitmapSegment:Vidoffset];
  1843.                    End;
  1844.  
  1845.  
  1846.                    Asm
  1847.                    OR Byte Ptr RunLength, 192
  1848.                    End;
  1849.  
  1850.                    BlockWrite(f,RunLength,1);
  1851.                    BlockWrite(f,LastByte,1);
  1852.  
  1853.                    LastByte:=NewByte;
  1854.                    End
  1855.                 Else
  1856.  
  1857.                 { How to deal with colours > 191. }
  1858.                     If (NewByte > 191) Then
  1859.                        Begin
  1860.                        Inc(ByteCount);
  1861.                        Inc(VidOffset);                { Point to next byte on screen }
  1862.                        RunLength:=193;
  1863.                        BlockWrite(f,RunLength,1);     { Write run length byte of 1  ! }
  1864.                        BlockWrite(f,NewByte,1);       { The ONLY way to get round }
  1865.                        LastByte:=NewByte;
  1866.                        End
  1867.                     Else
  1868.                         Begin
  1869.                         Inc(ByteCount);
  1870.                         Inc(vidoffset);
  1871.                         BlockWrite(f,NewByte,1);
  1872.                         LastByte:=NewByte;
  1873.                         End;
  1874.  
  1875.           Until ByteCount = PCXWidth;
  1876.  
  1877.           VidOffset:=LastOffset+320;
  1878.      End;
  1879.  
  1880.      {
  1881.      12 is Colourmap ID.
  1882.      }
  1883.  
  1884.      ColourMapID:=12;
  1885.      BlockWrite(f,ColourMapID,1);
  1886.  
  1887.      {
  1888.      Now write Palette R,G,B values to disk. The only reason
  1889.      I didn't implement :
  1890.  
  1891.      BlockWrite(F,Palette,SizeOf(Palette))
  1892.  
  1893.      was that all the palette entries had to be shifted LEFT
  1894.      twice (To represent a 16.7 million colour palette..) 
  1895.      
  1896.                                 DAMN!
  1897.      }
  1898.  
  1899.      For ColourCount:=0 to 255 do
  1900.          Begin
  1901.  
  1902.          RedValue:=ThePalette.   RedLevel[ColourCount] SHL 2;
  1903.          GreenValue:=ThePalette. GreenLevel[ColourCount] SHL 2;
  1904.          BlueValue:=ThePalette.  BlueLevel[ColourCount] SHL 2;
  1905.  
  1906.          BlockWrite(F,RedValue,1);
  1907.          BlockWrite(F,GreenValue,1);
  1908.          BlockWrite(F,BlueValue,1);
  1909.      End;
  1910.  
  1911.      Close(F);         { That's it - it's not over, not over yet .. :-) }
  1912. End;
  1913.  
  1914.  
  1915.  
  1916.  
  1917.  
  1918.  
  1919. {
  1920. Save a PCX file to disk.
  1921.  
  1922. Expects  :  Filename is the MS-DOS filespec , i.e. "C:\PICS\MYFILE.PCX"
  1923.            ThePalette specifies a PaletteType record to save to disk in
  1924.            the PCX file.
  1925.  
  1926. Returns  :  Nothing
  1927.  
  1928. Corrupts :  Don't know !!
  1929. }
  1930.  
  1931.  
  1932. Procedure SavePCX(filename:string;ThePalette: PaletteType);
  1933. Begin
  1934.      SaveAreaAsPCX(filename,ThePalette,0,0,320,200);
  1935. End;
  1936.  
  1937.  
  1938.  
  1939.  
  1940.  
  1941. {
  1942. **********************
  1943. MISCELLANEOUS ROUTINES
  1944. **********************
  1945. }
  1946.  
  1947. {
  1948. Wait for a certain number of vertical retraces, specified by
  1949. the number in TimeOut. (A vertical retrace occurs when the
  1950. monitor begins to draw the screen; If you wait for this
  1951. retrace and then update the screen your graphics will not
  1952. flicker - well not as much as before ;-) )
  1953.  
  1954.  
  1955. Corrupts AL,CX,DX
  1956. }
  1957.  
  1958.  
  1959. Procedure Vwait(TimeOut:word); Assembler;
  1960. Asm
  1961.          MOV CX,TimeOut         { CX = Number of times to wait }
  1962.  
  1963.          MOV DX,$3DA            { Port $3DA holds vertical & horizontal
  1964.                                   retrace status bits }
  1965. @WaitEnd:
  1966.          IN AL,DX               { Read port }
  1967.          TEST AL,8              { Test for bit 3 being set: If
  1968.                                   it is then that means the system may
  1969.                                   be in the middle of it's refresh
  1970.                                   and so writing to the screen now may
  1971.                                   cause flicker. }
  1972.          JNZ @WaitEnd           { If set, go back to @waitend }
  1973.  
  1974. {
  1975. When the routine gets to here it's the end of the retrace.
  1976. }
  1977.  
  1978. @WaitStart:
  1979.          IN AL,DX               { Read port again }
  1980.          TEST AL,8              { Is the bit set ? }
  1981.          JZ @WaitStart          { No ! So go back }
  1982.  
  1983.          DEC CX
  1984.          JNZ @WaitEnd          { Reduce count in CX, if <>0 go back
  1985.                                   to WaitEnd ! }
  1986.  
  1987. End;
  1988.  
  1989.  
  1990.  
  1991.  
  1992.  
  1993.  
  1994. {
  1995. Clear the Source Bitmap with Colour 0 (Always).
  1996.  
  1997. Expects : SourceBitmapSegment, SourceBitmapOffset to point to the source
  1998.           Bitmap (of course).
  1999.  
  2000. Returns : Nothing.
  2001.  
  2002. Corrupts : AX,CX,DI,ES
  2003.  
  2004.  
  2005. }
  2006.  
  2007. Procedure Cls; Assembler;
  2008. Asm
  2009.      MOV ES,SourceBitmapSegment
  2010.      MOV DI,SourceBitmapOffset
  2011.  
  2012.      MOV CX,4000         { 4000 x 16 byte moves are executed }
  2013.      DB $66
  2014.      XOR AX,AX           { XOR EAX,EAX - Colour 0 used to clear screen }
  2015.  
  2016. @ClearLoop:
  2017.      DB $66; STOSW       { STOSD }
  2018.      DB $66; STOSW
  2019.      DB $66; STOSW
  2020.      DB $66; STOSW
  2021.      DEC CX
  2022.      JNZ @ClearLoop
  2023.  
  2024. End;
  2025.  
  2026.  
  2027.  
  2028.  
  2029.  
  2030. {
  2031. Clear the screen with the graphics colour specified.
  2032.  
  2033. Expects : CurrentColour set to non-zero value
  2034.           Source Bitmap initialised with Bitmap
  2035.  
  2036. Returns : Nothing
  2037.  
  2038. Corrupts : AX,BX,CX,DI,ES
  2039. }
  2040.  
  2041. Procedure CCls(TheColour : byte); Assembler;
  2042. Asm
  2043.    MOV ES,SourceBitmapSegment
  2044.    MOV DI,SourceBitmapOffset
  2045.  
  2046.    MOV CX,4000
  2047.    MOV AH,TheColour
  2048.    MOV AL,AH
  2049.    MOV BX,AX
  2050.  
  2051.    DB $66; SHL AX,16            { SHL EAX,16 -> Move AH & AL into
  2052.                                   upper word of EAX}
  2053.    MOV AX,BX                    { Now EAX is fully set }
  2054.  
  2055. @FillLoop:
  2056.    DB $66; STOSW                { STOSD }
  2057.    DB $66; STOSW
  2058.    DB $66; STOSW
  2059.    DB $66; STOSW
  2060.    DEC CX
  2061.    JNZ @FillLoop                { You could use LOOP but I heard this
  2062.                                   method is faster }
  2063. End;
  2064.  
  2065.  
  2066.  
  2067.  
  2068.  
  2069. {
  2070. This is the initialisation part of the unit.
  2071. }
  2072.  
  2073. Begin
  2074.      SetSourceBitmapAddr($a000,0);
  2075.      DoubleBufferOff;
  2076.      Cls;                            { Flush video mem }
  2077.      MoveTo(0,0);                    { Graphics Cursor to top left }
  2078.      SetColour(1);                   { Use Colour 1 }
  2079.      UseFont(Font8x8);               { standard 8 x 8 }
  2080.  
  2081.      Writeln('NewGraph unit (C) 1995, 1996 Scott Tunstall. All rights');
  2082.      Writeln('reserved. Unauthorised editing/duplication of this code');
  2083.      Writeln('is PROHIBITED.');
  2084.      Writeln;
  2085.  
  2086. End.  { of unit }